﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ManagedCuda.BasicTypes;
using ManagedCuda;
using ManagedCuda.CudaBlas;
using MathNet.Numerics.LinearAlgebra.Single;
using System.Reflection;
using MathNet.Numerics.LinearAlgebra.Generic;

namespace cudaMatrix
{
    enum Direction { ToHost, ToDevice };

    public class CudaMatrix : DenseMatrix
    {
        internal CudaDeviceVariable<float> device;
        internal CudaBlas cublas;

        public CudaMatrix(int rows, int columns, float[] array) :base(rows, columns, array){
            cublas = BLASProvider.Instance;
            device = array;
        }

        internal void sync(Direction dir)
        {
            if (dir == Direction.ToDevice)
            {
                device = this.Data;
            }
            else if (dir == Direction.ToHost)
            {
                typeof(CudaMatrix).GetField("Data", BindingFlags.NonPublic).SetValue(this, device);
            }
        }

        protected override void DoMultiply(Matrix<float> other, Matrix<float> result)
        {
            var denseOther = other as CudaMatrix;
            var denseResult = result as CudaMatrix;

            if (denseResult == null)
            {
                denseResult = (CudaMatrix)MatrixFactory.createMatrix(this.RowCount, denseOther.ColumnCount, new float[this.RowCount * denseOther.ColumnCount]);
            }
            System.Diagnostics.Trace.WriteLine("CUDA MATRIX MULTIPLICATION");
            sync(Direction.ToDevice);
            denseOther.sync(Direction.ToDevice);
            cublas.Gemm(Operation.NonTranspose, Operation.NonTranspose, ColumnCount, denseOther.RowCount, RowCount, 1.0f, device, ColumnCount, denseOther.device, denseOther.ColumnCount, 1.0f, denseResult.device, denseResult.ColumnCount);
            denseResult.sync(Direction.ToHost);
        }
    }
}
